﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using VersionComparer.Configuration;
using System.Xml;
using VersionComparer.Base;
using VersionComparer.DataModel.Xml;
using VersionComparer.DataModel.FileSystem;

namespace VersionComparer.DataModel
{
    public class XmlFileItem : FileItem
    {
        string lastText;
        DateTime lastRead;

        #region constructors

        public XmlFileItem(string path, Guid id)
            : this(path)
        {
            Id = id;
        }
        public XmlFileItem(string path):base(path)
        {
            Path = path;
        }

        #endregion constructors

        #region properties
        public override bool AllowsChangingValue
        {
            get
            {
                return false;
            }
        }
        protected XmlDocument XmlDocument
        {
            get;
            private set;
        }
        public override bool RequiresUpdate
        {
            get
            {
                bool requiresUpdate;
                if (lastText == null)
                    return true;
                LoadFile(out requiresUpdate);
                return requiresUpdate;
            }
        }
        
        public override void Refresh()
        {
            bool requiresUpdate;
            LoadFile(out requiresUpdate);
            base.Refresh();
        }

        protected XmlNode XmlNode
        {
            get;
            set;
        }
        public string Path
        {
            get;
            set;
        }
        public bool Loaded
        {
            get;
            set;
        }

        bool hasChanged;
        public override bool HasChanged
        {
            get
            {
                if (String.IsNullOrEmpty(Path))
                    return false;
                hasChanged |= hasChanged || File.GetLastWriteTimeUtc(Path) != lastRead;
                return hasChanged;
            }
        }
        #endregion properties

        protected override ComparableItem[] CreateChilds()
        {
            ComparableItem[] childs = new ComparableItem[] { new XmlNodeItem(this, XmlNode) };

            return childs;
        }
        public virtual bool AskForSave(bool related)
        {
            if (!Settings.Current.AskBeforeSaving)
                return true;
            return AskForSave(related? RelatedFilePath: Path);
        }
        public virtual bool AskForSave(string path)
        {
            if (!Settings.Current.AskBeforeSaving)
                return true;
            return MessageBox.Show(string.Format("Are you sure you want to overwrite file {0}?", path), "Save", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;
        }
        public override bool Copy(bool save)
        {
            if (!save)
            {
                //CopyNode(this, RelatedItem  as XmlItem , true);
                return true;
            }
            try
            {
                string relatedItemPath = (RelatedItem as XmlFileItem).Path;
                if (AskForSave(relatedItemPath))
                {
                    File.WriteAllText(relatedItemPath, File.ReadAllText(Path));
                    return true;
                }
                else
                    return false;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }
        public void ForceUpdate()
        {
            lastRead = DateTime.MinValue;
        }
        public override bool Save()
        {
            try
            {
                DateTime fileLastWrite = File.GetLastWriteTimeUtc(Path);
                if (fileLastWrite > lastRead)
                {
                    if (MessageBox.Show("The file has been changed and may be different. Do you want to overwrite?", "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
                        return false;
                }
                File.Copy(Path, Path + ".bak", true);
                XmlDocument.Save(Path);
                lastRead = File.GetLastWriteTimeUtc(Path);
                ForceUpdate();
                return true;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }
        private void LoadXmlFromfile(object dom)
        {
            XmlDocument doc = dom as XmlDocument;
            try
            {

                doc.Load(Path);
                lastRead = File.GetLastWriteTimeUtc(Path);
                hasChanged = false;
                Loaded = true;
            }
            catch (Exception e)
            {
                try
                {
                    if (!string.IsNullOrEmpty(Path))
                        lastRead = File.GetLastWriteTimeUtc(Path);
                }
                catch { }
                doc.LoadXml(string.Format("<unableToLoad></unableToLoad>"));
                doc.DocumentElement.InnerText = string.Format("{0}: {1}", Path, e.Message);
            }
        }
        public override Difference DifferenceRelativeTo(ComparableItem item)
        {
            XmlFileItem other = item as XmlFileItem;
            if (other != null)
            {
                if (other.lastText == lastText)
                    return Difference.None;
                else
                    return Difference.None;
            }
            else
                return DataModel.Difference.Unmatched;
        }
        public XmlDocument LoadFile(out bool requiresUpdate)
        {
            XmlDocument newDoc = new XmlDocument();
            requiresUpdate = true;

            Difference = Difference.Unexistant;
            Loaded = false;
            if (Path != null)
            {
                try
                {
                    System.Threading.Thread thread = new System.Threading.Thread(LoadXmlFromfile);
                    thread.Start(newDoc);
                    if (!thread.Join(Settings.Current.LoadfileTimeout))
                        throw new Exception("Timeout while loading file");
                }
                catch (Exception e)
                {
                    lastText = null;
                    lastRead = DateTime.MinValue;
                    requiresUpdate = true;
                    newDoc.LoadXml(string.Format("<unableToLoad></unableToLoad>"));
                    newDoc.DocumentElement.InnerText = string.Format("{0}: {1}", Path, e.Message);

                }
                string currentText = newDoc.OuterXml;

                Difference = Difference.None;

                if (Loaded)
                {
                    if (currentText == lastText)
                        requiresUpdate = false;
                    else
                        lastText = currentText;
                }
                else
                {
                    requiresUpdate = true;
                    lastText = null;
                }
                if (requiresUpdate)
                {
                    XmlDocument = newDoc;
                    XmlNode = newDoc.DocumentElement;
                }
            }
            return newDoc;
        }
        protected override Image GetImage()
        {
            try
            {
                if (string.IsNullOrEmpty(Path))
                    return null;
                return Icon.ExtractAssociatedIcon(Path).ToBitmap();
            }
            catch
            {
                return null;
            }
        }
        public override bool ContainsString(bool ignoreCase, params string[] values)
        {
            if (base.ContainsString(ignoreCase, values))
                return true;
            if (lastText == null)
                return false;
            return lastText.FindInString(values, ignoreCase);
        }
       
    }
}
